package com.zhxin.nettylab.nio.chapter3;

import java.io.*;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
 * @ClassName NioServer
 * @Description //NioFileServer 文件传输实践 服务端示例
 * @Author singleZhang
 * @Email 405780096@qq.com
 * @Date 2020/12/4 0004 上午 10:48
 **/
public class NioFileServer {
    private static Selector selector;
    private Charset charset = Charset.forName("UTF-8");
    private ByteBuffer buffer = ByteBuffer.allocate(1024);

    /**
     * 内部类,服务端保存客户端 文件对象
     * */
    static class Client{

        String fileName;
        long fileLength;
        long startTime;
        InetSocketAddress remoteAddress;
        FileChannel outFileChannel;

    }

    private  Map<SelectableChannel,Client> clientMap = new HashMap<>();

    public static void main(String[] args) throws IOException {
        NioFileServer server = new NioFileServer();
        server.startServer();
    }

    /**
     * 服务端启动
     * */
    private void startServer() throws IOException {
        selector = Selector.open();

        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        ServerSocket serverSocket = serverSocketChannel.socket();
        serverSocket.bind(new InetSocketAddress(8989)); //将该通道对应的serverSocket绑定到port端口
        serverSocketChannel.configureBlocking(false);
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        while (selector.select() > 0){

            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> keyIterator = selectionKeys.iterator();

            while (keyIterator.hasNext()){
                SelectionKey key = keyIterator.next();

                //事件筛选处理
                if(key.isAcceptable()){
                    handleAccept(key);//处理客户端连接事件
                } else if(key.isReadable()){
                    handleClientData(key);//处理客户端发过来的数据
                }

                //移除选择键
                keyIterator.remove();
            }
        }
    }

    /**
     * 处理客户端连接事件
     * */
    private void handleAccept(SelectionKey key)  throws IOException{
        ServerSocketChannel server = (ServerSocketChannel) key.channel();

        SocketChannel socketChannel = server.accept();
        if(socketChannel != null){

            socketChannel.configureBlocking(false);//客户端新连接，设置非阻塞

            // 数据写入通道
            String msg = "Hello client!";
            socketChannel.write(ByteBuffer.wrap(msg.getBytes()));

            //为每条通道设置对应的client客户端兑现
            Client client = new Client();
            client.remoteAddress = (InetSocketAddress) socketChannel.getRemoteAddress();
            clientMap.put(socketChannel,client);
            System.out.println(socketChannel.getRemoteAddress() + "连接成功!");
        }
    }

    /**
     * 处理客户端发过来的数据
     * */
    private  void handleClientData(SelectionKey key)  throws IOException{
        SocketChannel socketChannel = (SocketChannel) key.channel();
        Client client = clientMap.get(socketChannel);

        // 从通道读取数据到缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(128);
        socketChannel.read(buffer);

        // 输出客户端端响应发送过来的消息
        byte[] data = buffer.array();
        String msg = new String(data).trim();
        System.out.println(" received msg from client："+"IP:"+socketChannel.getRemoteAddress() +" " + msg);

        int num = 0;
        try{
            buffer.clear();
            while ((num =socketChannel.read(buffer)) >0){
                buffer.flip();
                if(client.fileName == null){
                    //客户端传输过来的，先是文件名字
                    String fileName = charset.decode(buffer).toString();
                    String filePath = "E:/java_project/nettylab/src/main/resources";

                    File directory = new File(filePath);
                    if(!directory.exists()){
                        directory.mkdir();
                    }
                    client.fileName = fileName;
                    String fullName = directory.getAbsolutePath()+File.separatorChar+fileName;
                    System.out.println("客户端传过来的文件名:"+ fullName);

                    File file = new File(fullName);
                    FileChannel fileChannel = new FileOutputStream(file).getChannel();
                    client.outFileChannel = fileChannel;

                }else if(0 == client.fileLength){

                    //客户端传输过来的,其次是文件长度
                    long fileLength = buffer.getLong();
                    client.fileLength = fileLength;
                    client.startTime = System.currentTimeMillis();
                }else{

                    //客户端传输过来的,最后是文件内容
                    client.outFileChannel.write(buffer);
                }
                buffer.clear();
            }
            key.cancel();

        } catch (Exception e){
            key.cancel();
            e.printStackTrace();
        }

        if(num == -1){
            client.outFileChannel.close();
            key.cancel();
            System.out.println("文件传输完毕");
            long endTime = System.currentTimeMillis();
            System.out.println("传输时间:"+(endTime - client.startTime));
        }
    }
}
